package com.hollysys.elapi.parser.utils;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.hollysys.elapi.parser.common.ELNameEnum;
import com.hollysys.elapi.parser.common.ELTypeEnum;
import com.hollysys.elapi.parser.core.ELNode;
import com.hollysys.elapi.parser.core.ExpressLanguageParseException;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class ELNodeUtil {
    public static ELNode initThenNode() {
        ELNode elNode = new ELNode();
        elNode.setType(ELTypeEnum.elType.name());
        elNode.setName(ELNameEnum.THEN.name());
        return elNode;
    }

    public static ELNode initWhenNode() {
        ELNode elNode = new ELNode();
        elNode.setType(ELTypeEnum.elType.name());
        elNode.setName(ELNameEnum.WHEN.name());
        return elNode;
    }

    /**
     * 生成EL表达式字符串
     *
     * @return {@link String}
     * @throws ExpressLanguageParseException 表达语言解析异常
     */
    public static String generateEl(ELNode elNode) throws ExpressLanguageParseException {
        return getElString(elNode);
    }

    /**
     * 生成EL表达式字符串
     *
     * @param elNode el节点
     * @return {@link String}
     * @throws ExpressLanguageParseException 表达语言解析异常
     */
    private static String getElString(ELNode elNode) throws ExpressLanguageParseException {
        String name = elNode.getName();
        String conditionNodeId = elNode.getConditionNodeId();
        String aliasNodeId = elNode.getAliasNodeId();
        String data = elNode.getData();
        String nodeId = elNode.getNodeId();
        String type = elNode.getType();
        String tag = elNode.getTag();
        List<ELNode> children = elNode.getChild();
        // 校验参数
        validate(elNode);
        String elStr = nodeId;
        if (ELTypeEnum.elType.name().equals(type)) {
            // 遍历子节点，获取EL字符串
            List<String> list = new ArrayList<>();
            for (ELNode child : children) {
                // 校验子节点
                validate(child);
                String elString;
                if (ELTypeEnum.elType.name().equals(child.getType())) {
                    elString = getElString(child);
                } else {
                    elString = doWithSuffix(child.getNodeId(), child.getAliasNodeId(), child.getData(), child.getTag(), child.getName());
                }
                if (!StrUtil.isEmpty(elString)) {
                    list.add(elString);
                }
            }
            elStr = elOperate(name, conditionNodeId, list, tag);
        }

        return doWithSuffix(elStr, aliasNodeId, data, tag, name);
    }


    /**
     * 根据不同el操作,拼接EL需要的字符串
     *
     * @param name            名字
     * @param conditionNodeId 条件节点id
     * @param params          参数个数
     * @return {@link String}
     */
    private static String elOperate(String name, String conditionNodeId, List<String> params, String tag) {

        String elStr = "";
        if (CollectionUtil.isEmpty(params)) {
            return elStr;
        }
        String join = String.join(",", params);

        if (ELNameEnum.THEN.name().equals(name) || ELNameEnum.WHEN.name().equals(name)) {
            elStr = String.format("%s(%s)", name, join);
        }
        if (ELNameEnum.SWITCH.name().equals(name)) {
            elStr = String.format("%s(%s).TO(%s)", name, conditionNodeId, join);
        }
        if (ELNameEnum.IF.name().equals(name)) {
            if (!StrUtil.isEmpty(tag)) {
                conditionNodeId = String.format("%s.tag(\"%s\")", conditionNodeId, tag);
            }
            elStr = String.format("%s(%s,%s)", name, conditionNodeId, join);
        }
        return elStr;
    }


    /**
     * 后缀处理，id和data
     *
     * @param elString    el字符串
     * @param aliasNodeId 别名节点id
     * @param data        数据
     * @return {@link String}
     */
    private static String doWithSuffix(String elString, String aliasNodeId, String data, String tag, String name) {
        if (StrUtil.isEmpty(elString)) {
            return "";
        }
        if (!StrUtil.isEmpty(data)) {
            elString = String.format("%s.data(%s)", elString, data);
        }
        if (!StrUtil.isEmpty(tag) && !ELNameEnum.IF.name().equals(name)) {
            elString = String.format("%s.tag(\"%s\")", elString, tag);
        }
        if (!StrUtil.isEmpty(aliasNodeId)) {
            elString = String.format("%s.id(\"%s\")", elString, aliasNodeId);
        }
        return elString;
    }

    /**
     * 验证
     *
     * @param elNode el节点
     * @return {@link Boolean}
     * @throws ExpressLanguageParseException 表达语言解析异常
     */
    public static Boolean validate(ELNode elNode) throws ExpressLanguageParseException {
        String type = elNode.getType();
        String name = elNode.getName();
        String nodeId = elNode.getNodeId();
        String conditionNodeId = elNode.getConditionNodeId();
        List<ELNode> children = elNode.getChild();
        if (StrUtil.isEmpty(type)) {
            throw new ExpressLanguageParseException("type 不能为空");
        }
        if (ELTypeEnum.idType.name().equals(type) && StrUtil.isEmpty(nodeId)) {
            throw new ExpressLanguageParseException("type 是 idType ，必填节点id");
        }

        if (ELTypeEnum.elType.name().equals(type)) {
            if (CollectionUtil.isEmpty(children)) {
                throw new ExpressLanguageParseException("type 是 el表达式 时，必填子节点");
            }
            if (StrUtil.isEmpty(name)) {
                throw new ExpressLanguageParseException("type 是 el表达式 时，必填 name");
            }
            List<String> supportName = Arrays.asList(ELNameEnum.THEN.name(), ELNameEnum.SWITCH.name(), ELNameEnum.IF.name(), ELNameEnum.WHEN.name());
            if (!supportName.contains(name)) {
                throw new ExpressLanguageParseException("仅支持 THEN/SWITCH/IF/WHEN");
            }
            if (ELNameEnum.SWITCH.name().equals(name) || ELNameEnum.IF.name().equals(name)) {
                if (StrUtil.isEmpty(conditionNodeId)) {
                    throw new ExpressLanguageParseException("SWITCH/IF 组件里需要 conditionNodeId ");
                }
            }
        }
        return true;
    }
}
